package com.stottlerhenke.simbionic.editor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.SequenceInputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import com.stottlerhenke.simbionic.api.SB_Param;
import com.stottlerhenke.simbionic.common.SB_Logger;
import com.stottlerhenke.simbionic.common.debug.DMFieldMap;
import com.stottlerhenke.simbionic.common.debug.MFCSocketInputStream;
import com.stottlerhenke.simbionic.common.debug.MFCSocketOutputStream;
import com.stottlerhenke.simbionic.common.debug.SB_DebugMessage;
import com.stottlerhenke.simbionic.editor.FileManager;
import com.stottlerhenke.simbionic.editor.SB_Action;
import com.stottlerhenke.simbionic.editor.SB_Behavior;
import com.stottlerhenke.simbionic.editor.SB_Binding;
import com.stottlerhenke.simbionic.editor.SB_Breakpoint;
import com.stottlerhenke.simbionic.editor.SB_Constant;
import com.stottlerhenke.simbionic.editor.SB_Entity;
import com.stottlerhenke.simbionic.editor.SB_ErrorInfo;
import com.stottlerhenke.simbionic.editor.SB_Folder;
import com.stottlerhenke.simbionic.editor.SB_Frame;
import com.stottlerhenke.simbionic.editor.SB_Function;
import com.stottlerhenke.simbionic.editor.SB_Global;
import com.stottlerhenke.simbionic.editor.SB_Parameter;
import com.stottlerhenke.simbionic.editor.SB_Predicate;
import com.stottlerhenke.simbionic.editor.SB_TypeManager;
import com.stottlerhenke.simbionic.editor.SB_Variable;
import com.stottlerhenke.simbionic.editor.SimBionicEditor;
import com.stottlerhenke.simbionic.editor.UserObject;
import com.stottlerhenke.simbionic.editor.gui.api.DefaultValidator;
import com.stottlerhenke.simbionic.editor.gui.api.I_CompileValidator;
import com.stottlerhenke.simbionic.editor.parser.SB_ParserEditorInterface;
import com.stottlerhenke.simbionic.engine.debug.SB_DebugServer;

public class SB_ProjectBar extends JTabbedPane implements ActionListener
{
    public static final Class SB_RectangleClass = SB_Rectangle.class;
    public static final Class SB_ConditionClass = SB_Condition.class;
    public static final Class SB_ConnectorClass = SB_Connector.class;

    protected SimBionicEditor _editor;
    public SB_Catalog _catalog;   
    protected SB_TypeManager _typeManager;
    public SB_Descriptors _descriptors;

    public static final int SIM_VER = 13;
    // version 13 support OO (classes)

    public static final int SBP_VER = 8; 
    // version 2 stores sim file pathname
    // version 3 stores user data for variables
    // version 4 stores IP address
    // version 5 stores class spec
    // version 6 stores type IDs with classes, stores deleted class IDs
    // version 7 NOT stores type name for variables, return type name for functions
    // version 8 stores the cpp compile option

    public static final int APD_VER = 4; 
    // version 2 stores user data for variables
    // version 3 stores type name for variables, return type name for functions
    // version 4 NOT stores type name for variables, return type name for functions

    public static final int BTN_VER = 6; 
    // version 2 stores user data for variables
    // version 3 stores comments and label mode
    // version 4 stores new execution mode and interruptiblity
    // version 5 stores type name for variables, return type name for functions
    // version 6 NOT stores type name for variables, return type name for functions

    public static final int LOADING_SBP = 0;
    public static final int LOADING_APD = 1;
    public static final int LOADING_BTN = 2;

    protected static int _sbp_ver;
    protected static int _apd_ver;
    protected static int _btn_ver;
    protected static int _loading;

    protected JFileChooser _fcSBP;
    protected JFileChooser _fcXML;
    protected JFileChooser _fcH;
    protected JFileChooser _fcJava;
    
    public File _fileSBP;
    protected File _fileAPD;
    protected File _fileSIM;

    public static final String LOOPBACK_ADDR = "127.0.0.1";
    public static final int AI_PORT = 7242;
    public static final int MAX_CONNECT_TRIES = 15;
    public static final int CONNECT_TIMEOUT_SECS = 10;
    protected String _ipAddress = LOOPBACK_ADDR;
    protected boolean _useLoopback = true;
    protected boolean _doCPPCompile = true;

    public enum DebugMode {
        RUN, PAUSE, STOP, STEP_INTO, STEP_OVER, STEP_ONE_TICK, STEP_RUN_TO_FINAL
    }
    
    protected boolean _debugging = false;
    protected DebugMode _debugMode = DebugMode.STOP;
    protected boolean _querying = false;
    protected Color _transcriptColor = Color.BLACK;
    protected Socket _connectionToServer;
    protected MFCSocketOutputStream _out;
    protected MFCSocketInputStream _in;
    protected SB_Logger _logger;
    public SB_Debugger _debugger;
    protected Timer _timer;

    protected boolean _SBPModified = false;
    protected boolean _APDModified = false;

    private SB_SettingsDialog _settingsDialog;

    protected static XMLReader _xr = null;
    
    protected SB_LocalsTree _localsTree;

    protected List _changeListeners;

    public SB_ProjectBar(SimBionicEditor editor)
    {
        super(BOTTOM);
        ComponentRegistry.setProjectBar(this);
        _editor = editor;
        
        _changeListeners = new ArrayList();

        _catalog = createCatalog();
        JScrollPane scrollCatalog = new JScrollPane(_catalog);
        addTab("Catalog", scrollCatalog);

        _descriptors = new SB_Descriptors(editor);
        JScrollPane scrollDescriptors = new JScrollPane(_descriptors);
        addTab("Descriptors", scrollDescriptors);

        setMinimumSize(new Dimension(100, 50));
    }

    protected SB_Catalog createCatalog()
    {
        return new SB_Catalog(_editor);
    }

    protected SB_TabbedCanvas getTabbedCanvas()
    {
        return (SB_TabbedCanvas) ComponentRegistry.getContent();
    }

    public void setLocalsTree(SB_LocalsTree tree)
    {
        _localsTree = tree;
    }
    
    public static int getSBPVersion()
    {
        return _sbp_ver;
    }

    public static void setSBPVersion(int sbp_ver)
    {
        _sbp_ver = sbp_ver;
        _loading = LOADING_SBP;
    }

    public static int getAPDVersion()
    {
        return _apd_ver;
    }

    public static void setAPDVersion(int apd_ver)
    {
        _apd_ver = apd_ver;
        _loading = LOADING_APD;
    }

    public static int getBTNVersion()
    {
        return _btn_ver;
    }

    public static void setBTNVersion(int btn_ver)
    {
        _btn_ver = btn_ver;
        _loading = LOADING_BTN;
    }

    public static int getLoading()
    {
        return _loading;
    }
   
    /**
     * Registers a listener to be notified of changes to the model.
     * @param listener
     */
    public void addChangeListener(SB_ChangeListener listener)
    {
        _changeListeners.add(listener);
    }
    
    /**
     * Unregisters a listener so that it is no longer notified of changes
     * to the model.
     * @param listener
     */
    public void removeChangeListener(SB_ChangeListener listener)
    {
        _changeListeners.remove(listener);
    }

    protected JFileChooser getFileChooserSBP()
    {
        if (_fcSBP == null)
        {
            _fcSBP = new JFileChooser();
            _fcSBP.resetChoosableFileFilters();
            _fcSBP.setAcceptAllFileFilterUsed(false);
            _fcSBP.setFileFilter(new javax.swing.filechooser.FileFilter()
            {

                public String getDescription()
                {
                    return "SimBionic Project (*.sbp)";
                }

                public boolean accept(File f)
                {
                    if (f.isDirectory())
                    {
                        return true;
                    }

                    String ext = SB_ProjectBar.this.getExt(f);
                    if (ext != null)
                    {
                        if (ext.equals("sbp"))
                        {
                            return true;
                        } else
                        {
                            return false;
                        }
                    }
                    return false;
                }
            });
           
            _fcSBP.setCurrentDirectory(new File(System.getProperty("user.dir")));
        }
        return _fcSBP;
    }

    public SB_Catalog getCatalog()
    {
        return _catalog;
    }

    public void setCatalog(SB_Catalog catalog)
    {
        _catalog = catalog;
    }

    protected SB_SettingsDialog getSettingsDialog()
    {
        if (_settingsDialog == null)
            _settingsDialog = new SB_SettingsDialog();
        return _settingsDialog;
    }

    public void goToNextError()
    {
        SB_OutputBar outputBar = SB_OutputBar.getInstance();
        outputBar.setSelectedIndex(SB_OutputBar.BUILD_INDEX);
        SB_Output build = SB_OutputBar._build;
        int sel = build.getPrevError();
        build.setSel(sel);
        build.scrollToSel();
    }

    public void goToPreviousError()
    {
        SB_OutputBar outputBar = SB_OutputBar.getInstance();
        outputBar.setSelectedIndex(SB_OutputBar.BUILD_INDEX);
        SB_Output build = SB_OutputBar._build;
        int sel = build.getNextError();
        build.setSel(sel);
        build.scrollToSel();
    }

    /**
     * TODO: Handle run action
     */
    public void actionPerformed(ActionEvent e)
    {
        String command = e.getActionCommand();
        if (command == null)
            return;

        if (command.equals(SimBionicEditor.NEW_COMMAND))
            newProject();
        else if (command.equals(SimBionicEditor.OPEN_COMMAND))
            openProject();
        else if (command.equals(SimBionicEditor.SAVE_COMMAND))
            saveProject();
        else if (command.equals(SimBionicEditor.SAVEAS_COMMAND))
            saveProjectAs();
        else if (command.equals(SimBionicEditor.COMPILE_COMMAND))
            compileSIM();
        else if (command.equals(SimBionicEditor.SETTINGS_ITEM))
        {
            getSettingsDialog().initDialog();
            getSettingsDialog().setVisible(true);
        } else
            System.err.println("Error in SB_ProjectBar.actionPerformed:"
                    + " unknown action command: " + command);
    }

    public boolean isSBPModified()
    {
        return _SBPModified;
    }

    public void setSBPModified(boolean modified)
    {
        _SBPModified = modified;
        if (modified)
        {
            _editor.setDirty(true);
            System.out.println("SBP modified");
        } else
            _catalog.setBTNModified(false);
    }

    public boolean isAPDModified()
    {
        return _APDModified;
    }

    protected void setAPDModified(boolean modified)
    {
        _APDModified = modified;
        if (modified)
        {
            _editor.setDirty(true);
        }
    }

    public void newProject()
    {
        _editor.updateRecentItems();

        _fileSBP = null;
        _fileAPD = null;
        _fileSIM = null;

        setupTypeManager();

        _descriptors.newDescriptors();
        _catalog.newCatalog();
        setSelectedIndex(0);

        SB_ToolBar toolBar = (SB_ToolBar) ComponentRegistry.getToolBar();
        toolBar.clearStacks();
        
        setAPDModified(false);
        setSBPModified(false);
        _catalog.setBTNModified(false);

        _catalog.updateComboBehav();
    }

    public void openProject()
    {
        int returnVal = getFileChooserSBP().showOpenDialog(ComponentRegistry.getFrame());
        if (returnVal == JFileChooser.APPROVE_OPTION)
        {
            File file = getFileChooserSBP().getSelectedFile();
            if (!file.exists())
            {
                JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "'" + file.getPath()
                        + "' cannot be found.   ", "File Not Found", JOptionPane.WARNING_MESSAGE);
                return;
            }
            if (file.equals(_fileSBP)) // check if already open
                return;


            _fileSBP = file;
            _fileSIM = null;
            setupFiles();

            if (_editor._recentList != null && _editor._recentList.contains(_fileSBP))
                _editor.addRecentProject(_fileSBP);

            setupTypeManager();

            JFrame frame = ComponentRegistry.getFrame();
            frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            loadProject();
            frame.setCursor(Cursor.getDefaultCursor());
        }
    }

    public boolean loadProject(File file)
    {
        _fileSBP = file;
        _fileSIM = null;
        setupFiles();
        
        setupTypeManager();
        
        if (ComponentRegistry.isStandAlone())
        {
            ComponentRegistry.getFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
        loadProject();
        if (ComponentRegistry.isStandAlone())
        {
            ComponentRegistry.getFrame().setCursor(Cursor.getDefaultCursor());
        }
        return true;
    }

    protected void loadProject()
    {
        try
        {
            System.out.println("Opening " + _fileAPD);
            // open APD file
            FileInputStream in = new FileInputStream(_fileAPD);
            ObjectInputStream s = new ObjectInputStream(in);
            // open APD version number
            setAPDVersion(s.readInt());
            // check if APD version too new
            if (getAPDVersion() > APD_VER)
            {
                // warn user and abort
                JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                    "SimBionic must be updated to open APD file.  ", "Too New APD Version",
                    JOptionPane.ERROR_MESSAGE);
                System.exit(1);
            }
            // open actions, predicates, constants
            _catalog.openAPD(s);
            // open descriptors, set polymorphic globals
            _descriptors.openAPD(s);
            in.close();
        
            ComponentRegistry.getOutputBar().getBuild().addLine(new SB_Line("Loaded '" + _fileAPD.getPath() + "'"));            
        } catch (FileNotFoundException exception)
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "'" + _fileAPD.getPath()
                    + "' cannot be found.   ", "Missing APD File", JOptionPane.WARNING_MESSAGE);
            try
            {
                _catalog.loadBase();
            } catch (Exception e)
            {
                System.err.println("error parsing base.dat: " + exception.getMessage());
            }
            _catalog.newGlobals();
            _catalog.newConstants();
            _catalog.getRoot().remove(_catalog._behaviors);
            _descriptors.newDescriptors();
        } catch (IOException exeption)
        {
            System.err.println("i/o exception");
        }
        try
        {
            System.out.println("Opening " + _fileSBP);
            // open SBP and BTN files
            FileInputStream in = new FileInputStream(_fileSBP);
            ObjectInputStream s = new ObjectInputStream(in);
            // open SBP version number
            setSBPVersion(s.readInt());
            // check if SBP version too new
            if (getSBPVersion() > SBP_VER)
            {
                // warn user and abort
                JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                    "SimBionic must be updated to open SBP file.  ", "Too New SBP Version",
                    JOptionPane.ERROR_MESSAGE);
                System.exit(1);
            }
            
            // open behaviors, globals
            _catalog.openSBP(s);
            if (getSBPVersion() >= 2)
            {
                try
                {
                    _fileSIM = (File) s.readObject();
                    if (getSBPVersion() >= 4)
                    {
                        _ipAddress = (String) s.readObject();
                        _useLoopback = s.readBoolean();
                    }
                } catch (ClassNotFoundException exception)
                {
                    System.err.println("class not found");
                    exception.printStackTrace();
                }
                
                // cpp compile option
                if (getSBPVersion() >= 8)
                {
               		_doCPPCompile = s.readBoolean();
                }
            }
            
            // load deleted class IDs in SB_TypeManager
            if (getSBPVersion() >= 6)
            	_typeManager.deserialize(s);
            
            
            
            in.close();
            
            ComponentRegistry.getOutputBar().getBuild().addLine(new SB_Line("Loaded '" + _fileSBP.getPath() + "'"));            
            ComponentRegistry.getOutputBar().getBuild().addLine(new SB_Line("Load complete."));            
                   
        } catch (FileNotFoundException exception)
        {
            System.err.println("file not found");
            exception.printStackTrace();
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
            exception.printStackTrace();
        } catch (ClassNotFoundException ex)
        {
        	System.err.println("class not found");
        	ex.printStackTrace();
        }

        _catalog.updateProjectTitle();

        SB_ToolBar toolBar = ComponentRegistry.getToolBar();
        toolBar.clearStacks();

        setAPDModified(false);
        setSBPModified(false);
        _catalog.setBTNModified(false);
    }

    protected String getExt(File f)
    {
        String ext = null;
        String s = f.getName();
        int i = s.lastIndexOf('.');
        if (i > 0 && i < s.length() - 1)
        {
            ext = s.substring(i + 1).toLowerCase();
        }
        return ext;
    }

    protected void setupFiles()
    {
        setupFiles(true);
    }
    
    protected void setupFiles(boolean newLocation)
    {   
        String ext = getExt(_fileSBP);
        if (ext == null)
            _fileSBP = new File(_fileSBP.getPath() + ".sbp");
        String name = _fileSBP.getName();
        name = name.substring(0, name.length() - 4);
        _fileAPD = new File(_fileSBP.getParent() + /* File.separator */"//" + name + ".apd");
        // if (_fileSIM == null)
        _fileSIM = new File(_fileSBP.getParent() + /* File.separator */"//" + name + ".sim");
        if (newLocation)
            _catalog.setBTNModified(true);  // force them to save in the new location
    }

    public boolean saveProject()
    {
        if (_fileSBP == null)
            return saveProjectAs();

        if (!_fileSBP.getParentFile().exists())
        {
            if (!_fileSBP.getParentFile().mkdirs())
            {
                JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "'" + _fileSBP.getParent()
                        + "' cannot be found.   ", "Parent Directory Not Found",
                    JOptionPane.WARNING_MESSAGE);
                return false;
            }
        }

        SB_TabbedCanvas tabbedCanvas = (SB_TabbedCanvas) ComponentRegistry.getContent();
        getTabbedCanvas().storeLastValues();
        try
        {
            FileOutputStream out = null;
            ObjectOutputStream s;

            // save APD file
            out = new FileOutputStream(_fileAPD);
            s = new ObjectOutputStream(out);
            // save APD version number
            s.writeInt(APD_VER);
            // save actions, predicates, constants
            _catalog.saveAPD(s);
            // save descriptors
            _descriptors.saveAPD(s);
            s.flush();
            out.close();



            // save SBP and BTN files
            out = new FileOutputStream(_fileSBP);
            s = new ObjectOutputStream(out);
            // save SBP version number
            s.writeInt(SBP_VER);


            // save behaviors, globals
            _catalog.saveSBP(s);
            if (s != null)
            {
                s.writeObject(_fileSIM);
                s.writeObject(_ipAddress);
                s.writeBoolean(_useLoopback);
                s.writeBoolean(_doCPPCompile);
                
                // saves deleted class IDs for reuse later
                _typeManager.serialize(s);
                
                s.flush();
                out.close();
            }
        } catch (IOException exeption)
        {
            System.err.println("i/o exception");
            return false;
        }

        setAPDModified(false);
        setSBPModified(false);
        _catalog.setBTNModified(false);

        return true;
    }

    public boolean saveProjectAs()
    {
        if (_fileSBP == null)
        {
            getFileChooserSBP().setCurrentDirectory(new File(System.getProperty("user.dir")));
            getFileChooserSBP().setSelectedFile(new File(""));
        } else
            getFileChooserSBP().setSelectedFile(_fileSBP);
        int returnVal = getFileChooserSBP().showSaveDialog(ComponentRegistry.getFrame());
        if (returnVal == JFileChooser.APPROVE_OPTION)
        {
            File file = getFileChooserSBP().getSelectedFile();
            if (file.exists())
            {
                int n = JOptionPane.showConfirmDialog(ComponentRegistry.getFrame(), "'"
                        + file.getPath() + "' already exists.   \n" + "Do you want to replace it?",
                    "Save Project", JOptionPane.YES_NO_OPTION);
                if (n == JOptionPane.NO_OPTION)
                    return false;
            }
            _fileSBP = file;
            setupFiles();

            setAPDModified(true);
            setSBPModified(true);
            _catalog.setBTNModified(true);

            if (saveProject())
            {
                _catalog.updateProjectTitle();
                return true;
            }
        }
        return false;
    }

    /**
     * @return True if the save was successful and the user did not press
     *         cancel. if either the user selected "no" to save, or the user
     *         selected "yes" to save and the save was successful; return false
     *         if the user selected "cancel" or the save was unsuccessful.
     */
    public boolean saveIfModified()
    {
        if (_editor.isModified())
        {
            int n = JOptionPane.showConfirmDialog(ComponentRegistry.getFrame(),
                "Save changes to project?", "Save Project", JOptionPane.YES_NO_CANCEL_OPTION);
            if (n == JOptionPane.YES_OPTION)
            {
                return saveProject();
            }
            // if CANCEL, return false
            return (n == JOptionPane.NO_OPTION);
        } else
        {
            return true;
        }
    }

    /**
     * Compiles the project.
     * @return true on success
     */
    public boolean compileSIM()
    {
        return compileSIM(null,true,null);
    }
    
    /**
     * Compiles the project with a custom validator.
     * @return true on success
     */
    public boolean compileSIM(I_CompileValidator validator)
    {
        return compileSIM(null,true,validator);
    }

    public boolean compileSIM(SB_ErrorInfo errorInfo)
    {
        return compileSIM(errorInfo,true,null);
    }

    public boolean compileSIM(SB_ErrorInfo errorInfo, boolean clearOutput)
    {
        return compileSIM(errorInfo,clearOutput,null);
    }
    
    public boolean compileSIM(SB_ErrorInfo errorInfo,boolean clearOutput,I_CompileValidator validator)
    {

        File fileSIM;
        if (_fileSIM == null)
            fileSIM = new File(System.getProperty("user.dir") + /* File.separator */"//"
                    + "Untitled.sim");
        else
            fileSIM = _fileSIM;
        if (!fileSIM.getParentFile().exists())
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "'" + fileSIM.getParent()
                    + "' cannot be found.   ", "Parent Directory Not Found",
                JOptionPane.WARNING_MESSAGE);
            return false;
        }


        SB_Output build = SB_OutputBar._build;

        if (clearOutput)
            build.clearLines();
        else
            build.addLine(new SB_Line(""));
        build.addLine(new SB_Line("Compiling..."));
        build.requestFocus();

        // make sure output bar is visible with build tab selected

        ComponentRegistry.getOutputBar().setSelectedIndex(0);


        File fileTMP = null;
        File fileTMPPart1 = null;	// saves the part before classes
        File fileTMPPart2 = null;	// saves the part of classes
        File fileTMPPart3 = null;	// saves the part after classes
        try
        {
            String name = fileSIM.getName();
            name = name.substring(0, name.length() - 4);
            fileTMP = File.createTempFile(name, ".tmp", fileSIM.getParentFile());
            fileTMPPart1 = File.createTempFile(name, "-part1.tmp", fileSIM.getParentFile());
            fileTMPPart2 = File.createTempFile(name, "-part2.tmp", fileSIM.getParentFile());
            fileTMPPart3 = File.createTempFile(name, "-part3.tmp", fileSIM.getParentFile());
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }

        if (errorInfo == null)
            errorInfo = new SB_ErrorInfo();
        if (validator == null)
            validator = new DefaultValidator();
        try
        {
            validator.setOutputBar(build);
            validator.setErrorInfo(errorInfo);
            
            // open TMPPart1 file for writing the part before classes
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
                    fileTMPPart1)));

            // publish sim version number
            out.write("Sim-Format " + SIM_VER);
            out.newLine();

            // publish revision number
            int rev = FileManager.getInstance().getRevisionNumber(fileSIM);
            ++rev;
            out.write("File-Version " + rev);
            out.newLine();
            
            //resets inUse flags in classes, members and methods
            _catalog._rootPackage.resetInUseFlags();

            // publish hierarchies
            out.write("Hierarchies ");
            _descriptors.compileSIM(out,validator);

            // part1 finished
            out.flush();
            out.close();
            
            // open TMPPart3 file for writing the part after classes
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
                    fileTMPPart3)));
            
            // publish actions
            out.write("Actions ");
            DefaultMutableTreeNode actions = _catalog._actions;
            if (_doCPPCompile)
            	actions = deepCopyWithStrip("Core", actions, actions);
            
            _catalog.compileSIM(out, actions, null, validator);

            // publish predicates
            out.write("Predicates ");
            DefaultMutableTreeNode predicates = _catalog._predicates;
            // TODO: put this back
            if (_doCPPCompile)
            	predicates = deepCopyWithStrip("Core", predicates, predicates);
            
            _catalog.compileSIM(out, predicates, null, validator);

            // validate constants
            _catalog.validateConstants(errorInfo);

            // publish global variables
            _catalog.compileGlobals(out, errorInfo, validator);

            // publish behaviors
            out.write("Behaviors ");
            _catalog.compileSIM(out, _catalog._behaviors, errorInfo, validator);
            
            // part3 finished
            out.flush();
            out.close();
            
            // open TMPPart2 file for writing the part of classes
            // write out classes last because the inUse flags are now set properly
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
                    fileTMPPart2)));
            
            // last, publish classes
            _catalog.compileClasses(out, errorInfo, validator);

            // part2 finished
            out.flush();
            out.close();
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }

        // report number of errors and warnings
        String text = fileSIM.getName();
        String str_info = "";
        int ne = errorInfo._ne;
        int nw = errorInfo._nw;
        if (ne > 0) // at least one error
        {
            str_info = " (compile aborted)";

            // remove TMP file
            fileTMPPart1.delete();
            fileTMPPart2.delete();
            fileTMPPart3.delete();
        } else
        // SIM file successfully published
        {
            int nNodes = 0;
            int nConditions = 0;
            int nConnectors = 0;
            nNodes = _catalog.getDrawableCount(SB_RectangleClass);
            nConditions = _catalog.getDrawableCount(SB_ConditionClass);
            nConnectors = _catalog.getDrawableCount(SB_ConnectorClass);

            build.addLine(new SB_Line(""));
            build.addLine(new SB_Line("Compilation Statistics:"));
            String str_stat;
            build.addLine(new SB_Line("# of rectangles = " + nNodes));
            build.addLine(new SB_Line("# of conditions = " + nConditions));
            build.addLine(new SB_Line("# of connectors = " + nConnectors));
            
            mergeFiles(fileTMP, new File[]{fileTMPPart1, fileTMPPart2, fileTMPPart3});

            if (compareSIMFiles(fileSIM, fileTMP) == 0)
            {
                str_info = " (no change)";

                // remove TMP file
                fileTMP.delete();
                fileTMPPart1.delete();
                fileTMPPart2.delete();
                fileTMPPart3.delete();
            } else
            {
                // remove old SIM file and rename TMP file to SIM file
                fileSIM.delete();
                fileTMP.renameTo(fileSIM);
                fileTMPPart1.delete();
                fileTMPPart2.delete();
                fileTMPPart3.delete();
            }
        }
        String str_e = "error";
        if (ne == 0 || ne >= 2)
            str_e += "s";
        String str_w = "warning";
        if (nw == 0 || nw >= 2)
            str_w += "s";
        text += " - " + ne + " " + str_e + ", " + nw + " " + str_w;

        text += str_info;

        build.addLine(new SB_Line(""));
        if (ne > 0)
            build.addLine(new SB_Line(text, Color.red));
        else
            build.addLine(new SB_Line(text));
        build.repaint();

        build.updateFirstLastErrors();
        build.updateMenuItems();
        build.scrollToBottom();
        
        return (ne == 0);
    }
    
    public DefaultMutableTreeNode deepCopyWithStrip(String childToStrip, final DefaultMutableTreeNode oldNode, final DefaultMutableTreeNode uberParent)
    {
    	// see if we are at the stopping point -- only check shallow
    	if (childToStrip != null && oldNode.getParent() == uberParent)
    	{
    		Object obj = oldNode.getUserObject();
    		String name = null;
    		if (obj instanceof String)
    			name = (String)obj;
    		else if (obj instanceof SB_Folder)
    		{
    			SB_Folder folder = (SB_Folder)oldNode.getUserObject();
    			if (folder != null)
    			{
    				name = folder.getName();
    			}
    		}
			if (name != null && name.equals(childToStrip))
			{
					return null;
			}
    	}
    	
		DefaultMutableTreeNode copyNode = new DefaultMutableTreeNode();
		copyNode.setUserObject(oldNode.getUserObject());
		copyNode.setAllowsChildren(oldNode.getAllowsChildren());
		//copyNode.setParent((DefaultMutableTreeNode)oldNode.getParent());
		
		// now do children
    	for(int i=0;i<oldNode.getChildCount();i++)
    	{
    		DefaultMutableTreeNode newNode = deepCopyWithStrip(childToStrip, (DefaultMutableTreeNode)oldNode.getChildAt(i), uberParent);
    		if (newNode != null)
    		{
    			setDMTNParentForChildren(newNode);
    			copyNode.add( newNode );
    		}
    	}
    	
    	return copyNode;
    }
    
    private void setDMTNParentForChildren(DefaultMutableTreeNode node)
    {
    	for(int i=0;i<node.getChildCount();i++)
    	{
    		DefaultMutableTreeNode child = (DefaultMutableTreeNode)node.getChildAt(i);
    		child.setParent(node);
    		if (child.isLeaf() == false)
    			setDMTNParentForChildren(child);
    	}
    }
    
    protected DefaultMutableTreeNode stripDefaultActions(final DefaultMutableTreeNode actions)
    {
    	DefaultMutableTreeNode resultRoot = new DefaultMutableTreeNode();
    	resultRoot.setUserObject(actions.getUserObject());
    	resultRoot.setAllowsChildren(true);
    	resultRoot.setParent((DefaultMutableTreeNode)actions.getParent());
    	for(int i=0;i<actions.getChildCount();i++)
    	{
    		DefaultMutableTreeNode node = (DefaultMutableTreeNode)actions.getChildAt(i);
    		SB_Folder folder = (SB_Folder)node.getUserObject();
    		if (folder == null)
    			resultRoot.add(node);
    		else
    		{
	    		String name = folder.getName();
	    		if (name == null || !name.equals("Core"))
	    		{
	    			resultRoot.add(node);
	    		}
    		}
    	}
    	
    	return resultRoot;
    }

    protected int compareSIMFiles(File fileSIM, File fileTMP)
    {
        if (!fileSIM.exists())
            return -1;

        int result = 0;
        try
        {
            BufferedReader inSIM = new BufferedReader(new InputStreamReader(new FileInputStream(
                    fileSIM)));
            BufferedReader inTMP = new BufferedReader(new InputStreamReader(new FileInputStream(
                    fileTMP)));

            int verSIM, verTMP;
            String lineSIM = inSIM.readLine();
            String lineTMP = inTMP.readLine();
            int beginIndex = "Sim-Format ".length();
            verSIM = Integer.parseInt(lineSIM.substring(beginIndex));
            verTMP = Integer.parseInt(lineTMP.substring(beginIndex));
            if (verSIM > verTMP)
                result = 1;
            else if (verSIM < verTMP)
                result = -1;

            // ignore revision number
            lineSIM = inSIM.readLine();
            lineTMP = inTMP.readLine();

            lineSIM = inSIM.readLine();
            lineTMP = inTMP.readLine();
            while (result == 0 && lineTMP != null)
            {
                if (lineSIM == null)
                {
                    result = -1;
                    break;
                }

                result = lineSIM.compareTo(lineTMP);

                lineSIM = inSIM.readLine();
                lineTMP = inTMP.readLine();
            }
            if (result == 0 && lineSIM != null)
                result = -1;

            inSIM.close();
            inTMP.close();
        } catch (FileNotFoundException exception)
        {
            System.err.println("file not found");
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }
        return result;
    }

    public static String getPString(String text)
    {
        return text.length() + " " + text;
    }

    public static void writeTabs(BufferedWriter out, int n) throws IOException
    {
        for (int i = 0; i < n; ++i)
            out.write("\t");
    }

    public static String searchAndReplace(String expr, String oldText, String newText)
    {
        int len_old = oldText.length();
        int len_new = newText.length();
        if (len_old == 0)
            return expr;
        int len_expr = expr.length();
        boolean in_quotes = false;
        int i = 0, j;
        while (i < len_expr)
        {
            char c = expr.charAt(i);
            if (c == '\\') // ignore escape sequences, e.g. \"
                i += 2;
            else if (c == '"') // check for quotes
            {
                in_quotes = !in_quotes;
                ++i;
            } else if (!in_quotes)
            {
                // move index to beginning of word
                while (i < len_expr && !(isAlphaNumUnderline(c = expr.charAt(i))))
                {
                    if (c == '"') // entering quotes
                        break;
                    ++i;
                }
                for (j = 0; j < len_old; ++j) // search for old text
                {
                    if (i < len_expr && expr.charAt(i) == oldText.charAt(j))
                        ++i;
                    else
                        break;
                }
                // move index to end of word
                while (i < len_expr && (isAlphaNumUnderline(expr.charAt(i))))
                {
                    ++i;
                    j = 0; // search did not reach end of word
                }
                if (j == len_old) // old text found
                {
                    // replace old text with new text
                    i -= len_old;
                    expr = expr.substring(0, i) + newText + expr.substring(i + len_old);
                    i += len_new;
                    len_expr += len_new - len_old;
                }
            } else
                ++i;
        }
        return expr;
    }

    protected static boolean isAlphaNumUnderline(char c)
    {
        return Character.isLetterOrDigit(c) || c == '_';
    }

    protected void exportHeader()
    {
        File fileH = null;
        if (_fileSBP != null)
        {
            String name = _fileSBP.getName();
            name = name.substring(0, name.length() - 4);
            fileH = new File(_fileSBP.getParent() + /* File.separator */"//" + name + ".h");
        }
        FileManager.getInstance().getFileChooserH().setSelectedFile(fileH);
        FileManager.getInstance().getFileChooserH().setDialogTitle("Export C++ Header");
        int returnVal = FileManager.getInstance().getFileChooserH().showDialog(
            ComponentRegistry.getFrame(), "Export");
        if (returnVal == JFileChooser.APPROVE_OPTION)
        {
            fileH = FileManager.getInstance().getFileChooserH().getSelectedFile();
            String ext = getExt(fileH);
            if (ext == null)
                fileH = new File(fileH.getPath() + ".h");
            if (fileH.exists())
            {
                int n = JOptionPane
                        .showConfirmDialog(ComponentRegistry.getFrame(), "'" + fileH.getPath()
                                + "' already exists.   \n" + "Do you want to replace it?",
                            "Export C++ Header", JOptionPane.YES_NO_OPTION);
                if (n == JOptionPane.NO_OPTION)
                    return;
            }

            try
            {
                // open header file for writing
                BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
                        new FileOutputStream(fileH)));

                // write comment line
                String name = fileH.getName();
                out.write("// " + name + " : SimBionic generated constants");
                out.newLine();
                out.newLine();
                name = name.substring(0, name.length() - 2).toUpperCase();
                // SIM revision number
                int rev = FileManager.getInstance().getRevisionNumber(_fileSIM);
                if (rev == 0)
                    rev = 1;
                out.write("// sim revision number");
                out.newLine();
                out.write("#define SIM_REV " + rev);
                out.newLine();
                out.newLine();

                // export actions, predicates
                _catalog.exportHeader(out, name);

                out.close();
            } catch (IOException exception)
            {
                System.err.println("i/o exception");
                JOptionPane.showMessageDialog(this, "There was an error writing to the file: "
                        + exception.getMessage(), "File error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }


    /**
     * TODO: decide if this belongs here or in a subclass. TODO: This just adds
     * new items. We need to check for existing items, and do a confirm replace
     * (yes/no/yes to all/no to all). Of course this also does not do any sort
     * of ID replacement within the project, or matching IDs of old items to new
     * ones.
     */
    protected void mergeXML()
    {

        FileManager.getInstance().getFileChooserXML().setDialogTitle("Import and Merge XML");
        int returnVal = FileManager.getInstance().getFileChooserXML().showDialog(
            ComponentRegistry.getFrame(), "Import");
        if (returnVal == JFileChooser.APPROVE_OPTION)
        {
            File fileXML = FileManager.getInstance().getFileChooserXML().getSelectedFile();
            String ext = getExt(fileXML);
            if (ext == null)
                fileXML = new File(fileXML.getPath() + ".xml");
            if (!fileXML.exists())
            {
                JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "'" + fileXML.getPath()
                        + "' cannot be found.   ", "File Not Found", JOptionPane.WARNING_MESSAGE);
                return;
            }

            ComponentRegistry.getFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

            _fileSBP = null;
            _fileAPD = null;
            _fileSIM = null;
            try
            {
                if (_xr == null)
                {
                    _xr = XMLReaderFactory.createXMLReader();
                }
                MergeXMLHandler handler = new MergeXMLHandler();
                _xr.setContentHandler(handler);
                _xr.setErrorHandler(handler);
                FileReader editorReader = new FileReader(fileXML);
                try
                {
                    _xr.parse(new InputSource(editorReader));
                } catch (SAXException exception)
                {
                    System.err.println("SAX exception: " + exception.getMessage());
                }
                editorReader.close();
            } catch (FileNotFoundException exception)
            {
                exception.printStackTrace();
                System.err.println("file not found: " + fileXML.getPath());
            } catch (IOException exception)
            {
                exception.printStackTrace();
                System.err.println("i/o exception: " + fileXML.getPath());
            } catch (Exception exception)
            {
                exception.printStackTrace();
            }

            DefaultMutableTreeNode treeNode = _catalog.findNodeByName("Core Actions",
                _catalog._actions);
            SB_Folder folder = (SB_Folder) treeNode.getUserObject();
            folder.setEditable(false);
            treeNode = _catalog.findNodeByName("Core Predicates", _catalog._predicates);
            folder = (SB_Folder) treeNode.getUserObject();
            folder.setEditable(false);

            SB_ToolBar toolBar = ComponentRegistry.getToolBar();
            // toolBar.clearStacks();

            ComponentRegistry.getFrame().setCursor(Cursor.getDefaultCursor());

            setAPDModified(true);
            setSBPModified(true);
            _catalog.setBTNModified(true);
            // if (_editor._saveAction!=null)
            // _editor._saveAction.setEnabled(true);

            _catalog.updateComboBehav();
            SB_TabbedCanvas tabbedCanvas = (SB_TabbedCanvas) ComponentRegistry.getContent();
            tabbedCanvas._behavior = null;
            tabbedCanvas.setBehavior(_catalog._main, false);
        }
    }

    /**
     * Sends the given message to the debug server.
     * @param msg
     */
    public void sendMsg(SB_DebugMessage msg)
    {
      byte[] msgBytes = msg.serialize(_logger);

      ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
      MFCSocketOutputStream tempOut = new MFCSocketOutputStream(byteOut);
      try
      {
        tempOut.writeByte(0); //Packet type
        tempOut.writeMFCInt( msgBytes.length ); //PacketSize
        tempOut.write(msgBytes);

        _out.write(byteOut.toByteArray());
        _logger.log(" [[ sent message " + msg.GetTypeName() + " to server ]]",SB_Logger.DEBUGGER);;
    }
      catch( IOException e)
      {
        _logger.log("!! " + e.toString(), SB_Logger.ERROR);
      }
    }

    /**
     * Checks to see if any message has been received
     * from the debug server.  Does not block.  Use
     * waitForMessage if you want to wait until a given
     * message type is received.
     * @return the message, or null if no message has been received
     * @throws IOException
     */
    public SB_DebugMessage receiveMsg() throws IOException
    {
      SB_DebugMessage msg = null;

        if(_in.available() > 0 )
        {
          byte packetType = _in.readByte();
          int dataSize = _in.readMFCInt();
    
          int msgType = _in.readByte();
          msg = new SB_DebugMessage(msgType);
          msg.deserialize(_in, _logger);
        }
      
      if (msg != null)
  	  {
  		_logger.log(" [[ received message " + msg.GetTypeName() + " from server. ]]", SB_Logger.DEBUGGER);
  	  }

      return msg;
    }
    
    /**
     * Blocks while waiting for a message of a specified type (or range of types) to arrive
     * from a debug client.
     * @param firstMsgType the start of the desired range of message types (inclusive)
     * @param lastMsgType the end of the desired range of message types (inclusive);
     *						-1 indicates a single desired message type == firstMsgType
     * @param timeout if positive, indicates the time in seconds to wait for the message before
     *					giving up
     * @return the received message, or null if it timed out or a eng_shutdown message was received
     */
    public SB_DebugMessage waitForMessage(int firstMsgType,
                                          int lastMsgType /*=-1*/,
                                          int timeout /*= SB_DebugServer::NO_TIMEOUT*/)
    {
      SB_DebugMessage msg = null;

      if (lastMsgType == -1)
        lastMsgType = firstMsgType;

      // wait for message or timeout, whichever comes first
      double waitSoFar;
      Date startTick = new Date();

      do
      {
        if (msg != null)
        {
            SB_OutputBar._debug.addLine(new SB_Line("Discarded message type " + msg.GetTypeName() + " while waiting.",Color.RED));
        }

        try {
          Thread.sleep(100);
        } catch(Exception ex) {
          _logger.log(ex.toString());
        }

        try {
            msg = receiveMsg();
        } catch (IOException e) {
            _logger.log(e.toString());
            return null;    // connection has failed
        }

        waitSoFar = (new Date().getTime() - startTick.getTime()) / 1000.0;

      } while (((msg == null) || (msg.GetMsgType() < firstMsgType) || (msg.GetMsgType() > lastMsgType)) &&
                       ((timeout == SB_DebugServer.NO_TIMEOUT) || (waitSoFar < timeout)));

      if (waitSoFar >= timeout) {
          _logger.log("[[ timed out while waiting for message from server ]]",SB_Logger.DEBUGGER);
          SB_OutputBar._debug.addLine(new SB_Line("Timed out while waiting for message from server.", Color.RED));          
      }
      
      if (msg != null)
        _logger.log(" [[ received message '" + msg.GetTypeName() + "' ]]",SB_Logger.DEBUGGER);

      return msg;

    }

    /**
     * Connects to the debug server and puts the client in
     * debug mode.
     */
    public void connectDebug()
	{
        if (_debugging) {
            Toolkit.getDefaultToolkit().beep();
            return;
        }

        SB_OutputBar.getInstance().setSelectedIndex(SB_OutputBar.DEBUG_INDEX);

        if (!connectToServer())
            return;

        _debugging = true;
        _querying = false;
        _debugMode = DebugMode.PAUSE;   // start in pause mode
        
        _debugger = new SB_Debugger(_editor);
        ((SimBionicFrame)ComponentRegistry.getFrame()).setDebugModeOn(_debugger);
        
        if (_timer == null) {
        	_timer = new Timer(50, new DebugServerListener());
        }        
        _timer.start();
	}
    
    public void stepOverDebug()
    {
    	if (!_debugging || _querying) {
    		Toolkit.getDefaultToolkit().beep();
    		return;
    	}

    	_debugMode = DebugMode.STEP_OVER;
        
        _debugger.clearChangedVars();
    	
        DMFieldMap fields = new DMFieldMap();
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_STEP, fields) );
    
    	updateDebugToolbar();
    }
    
    public void stepOneTickDebug()
    {
    	if (!_debugging || _querying) {
    		Toolkit.getDefaultToolkit().beep();
    		return;
    	}

        _debugMode = DebugMode.STEP_ONE_TICK;
        
        _debugger.clearChangedVars();
    	
    	long entityID = _debugger.getCurrentEntity();            	
    	DMFieldMap fields = new DMFieldMap();    	
    	fields.ADD_LONG_FIELD("stepEntity", entityID);
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_STEP_ONE_TICK, fields) );
        
        updateDebugToolbar();
    }
    
    public void stepIntoDebug()
    {
    	if (!_debugging || _querying) {
    		Toolkit.getDefaultToolkit().beep();
    		return;
    	}
    	
        _debugMode = DebugMode.STEP_INTO;
    	
        _debugger.clearChangedVars();
    	
        DMFieldMap fields = new DMFieldMap();
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_STEP_INTO, fields) );
        
        updateDebugToolbar();
    }
    
    public void runToFinal()
    {
        if (!_debugging || _querying) {
            Toolkit.getDefaultToolkit().beep();
            return;
        }
        
        _debugMode = DebugMode.STEP_RUN_TO_FINAL;
        
        _debugger.clearChangedVars();

        long entityID = _debugger.getCurrentEntity();
        int frameID = _debugger.getCurrentFrame();
        
        //send gui_run_to_final
        DMFieldMap fields = new DMFieldMap();
        fields.ADD_INT_FIELD("frame", frameID);
        fields.ADD_LONG_FIELD("stepEntity", entityID);
        
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_RUN_TO_FINAL, fields));
        
        updateDebugToolbar();
    }
    
    public void startDebug()
    {
        if (!_debugging)
        {
            Toolkit.getDefaultToolkit().beep();
            return;
        }
        
        _debugMode = DebugMode.RUN;

        // send gui_start
        DMFieldMap fields = new DMFieldMap();
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_START, fields) );   
        
        updateDebugToolbar();
    }
    
    public void pauseDebug()
    {
        if (!_debugging)
        {
            Toolkit.getDefaultToolkit().beep();
            return;
        }
        
        _debugMode = DebugMode.PAUSE;

        // send gui_pause
        DMFieldMap fields = new DMFieldMap();
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_PAUSE, fields) );   
        
        updateDebugToolbar();
    }
    
    public void stopDebug()
    {
        if (!_debugging) {
            Toolkit.getDefaultToolkit().beep();
            return;
        }
        
        _debugMode = DebugMode.STOP;

        // send gui_stop
        DMFieldMap fields = new DMFieldMap();
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_STOP, fields) );
        
        // wait for eng_shutdown_ok
        SB_DebugMessage msg = waitForMessage(SB_DebugMessage.kDBG_ENG_SHUTDOWN_OK, -1, 3);
     
        closeDebug();
    }
    
    protected void closeDebug()
    {
        _timer.stop();
        
        try {
            _connectionToServer.close();
        } catch (IOException e) {
            SB_OutputBar._debug.addLine(new SB_Line("!! " + e.toString()));
        }
    
        _debugger._entities.clear();
        _debugger._execStackTable._tableModel._entity = null;
        
        _catalog.clearRunningState();
        getTabbedCanvas().getActiveCanvas().repaint();
        
        _debugging = false;
        ((SimBionicFrame)ComponentRegistry.getFrame()).setDebugModeOff(_debugger);
    }
    
    protected void updateDebugToolbar()
    {
        _editor.startAction.setEnabled(_debugMode != DebugMode.RUN);
        _editor.stepIntoAction.setEnabled(_debugMode != DebugMode.RUN);
        _editor.stepOverAction.setEnabled(_debugMode != DebugMode.RUN);
        _editor.pauseAction.setEnabled(_debugMode == DebugMode.RUN);
        
        boolean entities = _debugger._entities.size() > 0;
        
        _editor.stepOneTickAction.setEnabled((_debugMode != DebugMode.RUN) && entities);  
        _editor.runToFinalAction.setEnabled((_debugMode != DebugMode.RUN) && entities);
    }

    public void setLocalDebug(String varName, SB_Param varValue)
    {    	
    	int entitySelectedRow = _debugger._entitiesTable.getSelectedRow();
    	long entity = Long.parseLong((String)_debugger._entitiesTable.getValueAt(entitySelectedRow, 0));
    	int frameSelectedRow = _debugger._execStackTable.getSelectedRow();
    	int frame = Integer.parseInt((String)_debugger._execStackTable.getValueAt(frameSelectedRow, 0));
    	DMFieldMap fields = new DMFieldMap();
    	
    	fields.ADD_LONG_FIELD("entity", entity);
    	fields.ADD_INT_FIELD("frame", frame);
    	fields.ADD_STR_FIELD("varName", varName);
    	try{
    		fields.ADD_PARAM_FIELD("varValue", varValue);
    	} catch(Exception e){e.printStackTrace();}
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_SET_LOCAL, fields) );
    }
    
    public void addToWatchDebug()
    {
    	int entitySelectedRow = _debugger._entitiesTable.getSelectedRow();
    	long entity = Long.parseLong((String)_debugger._entitiesTable.getValueAt(entitySelectedRow, 0));
    	
    	DMFieldMap fields = new DMFieldMap();
    	fields.ADD_LONG_FIELD("entity", entity);
    	
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_ADD_TO_WATCH, fields) );
    }
    
    public void removeFromWatchDebug()
    {
    	int entitySelectedRow = _debugger._entitiesTable.getSelectedRow();
    	long entity = Long.parseLong((String)_debugger._entitiesTable.getValueAt(entitySelectedRow, 0));
    	
    	DMFieldMap fields = new DMFieldMap();
    	fields.ADD_LONG_FIELD("entity", entity);
    	
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_REMOVE_FROM_WATCH, fields) );
    }
    
    public void setGlobalDebug(String varName, SB_Param varValue)
    {	
    	int entitySelectedRow = _debugger._entitiesTable.getSelectedRow();
    	long entity = Long.parseLong((String)_debugger._entitiesTable.getValueAt(entitySelectedRow, 0));
    	int frameSelectedRow = _debugger._execStackTable.getSelectedRow();
    	int frame = Integer.parseInt((String)_debugger._execStackTable.getValueAt(frameSelectedRow, 0));
    	
    	DMFieldMap fields = new DMFieldMap();
    	
    	fields.ADD_LONG_FIELD("entity", entity);
    	fields.ADD_INT_FIELD("frame", frame);
    	fields.ADD_STR_FIELD("varName", varName);
    	try{
    		fields.ADD_PARAM_FIELD("varValue", varValue);
    	} catch(Exception e){e.printStackTrace();}
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_SET_GLOBAL, fields) );
    }
    
    public void addBreakVarDebug(SB_Breakpoint bp){
    	DMFieldMap fields = new DMFieldMap();
    	
    	fields.ADD_LONG_FIELD("entity", bp._entityId);
    	if(!bp._behavior.equals("Global")){
    		fields.ADD_STR_FIELD("behavior", bp._behavior);
    	}
    	else{
    		fields.ADD_STR_FIELD("behavior", "");
    	}
    	fields.ADD_STR_FIELD("variable", bp._varName);
    	fields.ADD_INT_FIELD("breakpointId", bp._breakpointId);
    	fields.ADD_INT_FIELD("iterations", bp._iterations);
    	fields.ADD_SA_FIELD("polyIndices", new ArrayList());
    	fields.ADD_STR_FIELD("constraint", "");
    	
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_ADD_BREAK_VAR, fields) );
    }
    
    public void addBreakElemDebug(SB_Breakpoint bp){
    	DMFieldMap fields = new DMFieldMap();
    	
    	fields.ADD_LONG_FIELD("entity", bp._entityId);
   		fields.ADD_STR_FIELD("behavior", bp._behavior);
    	fields.ADD_INT_FIELD("breakpointId", bp._breakpointId);
    	fields.ADD_INT_FIELD("iterations", bp._iterations);
    	fields.ADD_SA_FIELD("polyIndices", new ArrayList());
    	fields.ADD_STR_FIELD("constraint", "");
    	fields.ADD_INT_FIELD("elemId", bp._elemId);
    	fields.ADD_INT_FIELD("type", bp._type);
    	
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_ADD_BREAK_ELEM, fields) );
    }
    
    public void removeBreakpointDebug(SB_Breakpoint bp){
    	DMFieldMap fields = new DMFieldMap();

    	fields.ADD_INT_FIELD("breakpointId", bp._breakpointId);
    	
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_REMOVE_BREAKPOINT, fields) );
    }
    
    public void disableBreakpoint(int breakpointId){
    	DMFieldMap fields = new DMFieldMap();

    	fields.ADD_INT_FIELD("breakpointId", breakpointId);
    	
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_DISABLE_BREAKPOINT, fields) );
    }

    public void enableBreakpoint(int breakpointId){
    	DMFieldMap fields = new DMFieldMap();

    	fields.ADD_INT_FIELD("breakpointId", breakpointId);
    	sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_ENABLE_BREAKPOINT, fields) );
    }
    
    /**
     * Opens a connection to the SimBionic debug server.
     * @return true iff the connection was successfully established
     */
    protected boolean connectToServer()
    {
        SB_Output debug = SB_OutputBar._debug;
       	debug.clearLines();

       	if (_fileSIM == null)
       	{
       		debug.addLine(new SB_Line("No sim file specified.", Color.red));
       		return false;
       	}
       	else if (!_fileSIM.exists())
       	{
       	    // try looking in the same location as the SBP file
       	    _fileSIM = new File(_fileSBP.getPath().replace(".sbp",".sim"));
       	}
        
       	_logger = new SB_Logger();
       	_logger.register(System.out, 2);
       	
        debug.addLine(new SB_Line("Connecting to debug server..."));
       	
       	// try repeatedly to contact the debug server
       	int tryNum = 0;
       	while (tryNum < MAX_CONNECT_TRIES) {
            try {
            	_connectionToServer = new Socket();
            	_connectionToServer.connect(new InetSocketAddress(_ipAddress, AI_PORT), CONNECT_TIMEOUT_SECS*1000);
    
                _out = new MFCSocketOutputStream(_connectionToServer.getOutputStream());
                _in = new MFCSocketInputStream(_connectionToServer.getInputStream() );
                break;
    	    } catch (IOException e) {
    	    	debug.addLine(new SB_Line("Trying to connect to server...", Color.red));
    	    }
    	    ++tryNum;
       	}
       	if (!_connectionToServer.isConnected())
       	    return false;
       	
     	debug.addLine(new SB_Line("Opened connection to server."));
    	
        DMFieldMap fields = new DMFieldMap();
        fields.ADD_STR_FIELD( "simfileName", _fileSIM.getName() );
        fields.ADD_INT_FIELD("simfileVersion", FileManager.getInstance().getRevisionNumber(_fileSIM));
        fields.ADD_INT_FIELD( "simFormatVersion", SIM_VER );
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_INIT, fields) );
     	
    	SB_DebugMessage msg = null;

    	// do the initial handshake and version checking
    	while (true)
    	{
    		if (msg != null)
    		{
    			if (msg.GetMsgType() == SB_DebugMessage.kDBG_ENG_INIT_OK)
    			{
    				debug.addLine(new SB_Line("Handshake with server complete."));
    				_editor._breakpointFrame.initBreakpoints();
    				return true;
    			}
    			else if (msg.GetMsgType() == SB_DebugMessage.kDBG_ENG_INIT_FAILED)
    			{
    				int version = msg.GetIntField("simfileVersion");
    				String filename = msg.GetStringField("simfileName");
     				debug.addLine(new SB_Line("Received eng_init_failed: mismatched sim-files (" + 
     				        _fileSIM.getName() + " v" + FileManager.getInstance().getRevisionNumber(_fileSIM) + " vs " +
     				        filename + " v" + version + ")!", Color.red));
       				try
					{
       					_connectionToServer.close();
					}
    			    catch (IOException e)
				    {
				    	debug.addLine(new SB_Line("!! " + e.toString()));
				    	return false;
				    }
     				return false;
    			}
    			else
    			{
    				debug.addLine(new SB_Line("Received unwanted msg while waiting for response: eng_init_ok", Color.red));
    			}
    		}

    		try {
                msg = receiveMsg();
            } catch (IOException e) {
                debug.addLine(new SB_Line("Connection to server failed!", Color.red));
                return false;
            }
    	}
   }

    class SimBionicXMLHandler implements ContentHandler, ErrorHandler
    {
        protected Stack _qNames;

        protected String _text;

        protected String _main;

        protected int _initialId;

        protected int _cx, _cy;

        protected int _startId, _endId;

        protected int _x1, _y1, _x2, _y2;

        // //////////////////////////////////////////////////////////////////
        // ContentHandler events.
        // //////////////////////////////////////////////////////////////////
        public void startDocument()
        {
            _catalog.clearCatalog();
            _descriptors.clearDescriptors();
            setSelectedIndex(0);

            _qNames = new Stack();
        }

        public void endDocument()
        {
            ((DefaultTreeModel) _catalog.getModel()).reload();
            ((DefaultTreeModel) _descriptors.getModel()).reload();
            _descriptors.expandAll();

            _qNames = null;
            _text = null;
            _main = null;
        }

        public void startElement(String uri, String name, String qName, Attributes atts)
        {
            _text = null;

            // System.out.println("tag = " + name);
            if (name.equals("actions"))
                _qNames.push(_catalog._actions);
            else if (name.equals("predicates"))
                _qNames.push(_catalog._predicates);
            else if (name.equals("behaviors"))
                _qNames.push(_catalog._behaviors);
            else if (name.equals("globals"))
                _qNames.push(_catalog._globals);
            else if (name.equals("constants"))
                _qNames.push(_catalog._constants);
            else if (name.equals("locals"))
            {
                SB_Polymorphism poly = (SB_Polymorphism) _qNames.peek();
                _qNames.push(poly._locals);
            } else if (name.equals("nodes") || name.equals("conditions"))
            {
                SB_Polymorphism poly = (SB_Polymorphism) _qNames.peek();
                _qNames.push(poly._elements);
            } else if (name.equals("connectors"))
            {
                SB_Polymorphism poly = (SB_Polymorphism) _qNames.peek();
                _qNames.push(poly._connectors);
            } else if (name.equals("descriptors"))
                _qNames.push(_descriptors.getRoot());
            else if (name.equals("action"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Action());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("predicate"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Predicate());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("behavior") && _qNames.get(1).equals("sbp"))
            {
                SB_Behavior behavior = new SB_Behavior();
                behavior.clearPolys();
                behavior.setEditor(_editor);
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(behavior);
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("poly"))
            {
                int size = _qNames.size();
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _qNames.get(size - 2);
                SB_Behavior behavior = (SB_Behavior) treeNode.getUserObject();
                SB_Polymorphism poly = new SB_Polymorphism(behavior);
                poly._indices.removeAllElements();
                behavior.addPoly(poly);
                _qNames.push(poly);
            } else if (name.equals("folder"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Folder());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("param"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Parameter());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("global"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Global(""));
                _catalog._globals.add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("constant"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Constant(""));
                _catalog._constants.add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("local"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Variable());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("node"))
            {
                SB_Rectangle node = new SB_Rectangle();
                ((SB_ElementComposite) _qNames.peek())._drawables.add(node);
                _qNames.push(node);
            } else if (name.equals("condition"))
            {
                SB_Condition condition = new SB_Condition();
                ((SB_ElementComposite) _qNames.peek())._drawables.add(condition);
                _qNames.push(condition);
            } else if (name.equals("connector"))
            {
                SB_Connector connector = new SB_Connector();
                if (_startId != -1)
                    connector._startElement = (SB_Element) _qNames.peek();
                else
                    connector.p1.setLocation(_x1, _y1);
                int size = _qNames.size();
                ((SB_ConnectorComposite) _qNames.get(size - 2))._drawables.add(connector);
                _qNames.push(connector);
            } else if (name.equals("binding"))
            {
                _qNames.push(new SB_Binding("", ""));
            } else if (name.equals("category"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Category());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else if (name.equals("descriptor"))
            {
                DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new SB_Descriptor());
                ((DefaultMutableTreeNode) _qNames.peek()).add(treeNode);
                _qNames.push(treeNode);
            } else
                _qNames.push(name);
        }

        public void endElement(String uri, String name, String qName)
        {
            if (_text != null)
                processText();

            if (name.equals("binding"))
            {
                int size = _qNames.size();
                ((SB_BindingsHolder) _qNames.get(size - 3)).addBinding((SB_Binding) _qNames.peek());
            } else if (name.equals("connectors"))
            {
                SB_ConnectorComposite connectors = (SB_ConnectorComposite) _qNames.peek();
                connectors.updateTwoWay();
            }
            _qNames.pop();
        }

        public void characters(char ch[], int start, int length)
        {
            if (_qNames.empty())
                return;
            if (!(_qNames.peek() instanceof String))
                return;

            String text = new String(ch, start, length);
            if (_text == null)
                _text = text;
            else
                _text += text;
        }

        protected void processText()
        {
            if (_qNames.empty())
                return;
            if (!(_qNames.peek() instanceof String))
                return;

            String qName = (String) _qNames.peek();
            String text = _text;

            int size = _qNames.size();
            if (size >= 2 && _qNames.get(size - 2) instanceof DefaultMutableTreeNode)
            {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _qNames.get(size - 2);
                UserObject userObject = (UserObject) treeNode.getUserObject();
                if (qName.equals("name"))
                {
                    userObject.setName(text);
                    if (userObject instanceof SB_Category)
                    {
                        SB_Global global = new SB_Global("g" + text);
                        global.setType(SB_Variable.kString);
                        global.setPolymorphic(true);
                        treeNode = new DefaultMutableTreeNode(global);
                        _catalog._globals.add(treeNode);
                    }
                } else if (userObject instanceof SB_Function)
                {
                    SB_Function function = (SB_Function) userObject;
                    if (qName.equals("id"))
                        function.setId(Integer.parseInt(text));
                    else if (qName.equals("descr"))
                        function.setDescription(text);
                    else if (qName.equals("core"))
                        function.setCore(Integer.parseInt(text) != 0);
                    else if (qName.equals("ret"))
                    {
                        ((SB_Predicate) function).setRetType(Integer.parseInt(text));
                    }
                    else if (qName.equals("exec"))
                        ((SB_Behavior) function).setExec(Integer.parseInt(text));
                    else if (qName.equals("interrupt"))
                        ((SB_Behavior) function).setInterrupt(Integer.parseInt(text));
                } else if (userObject instanceof SB_Variable)
                {
                    SB_Variable var = (SB_Variable) userObject;
                    if (qName.equals("type"))
                    {
                        var.setType(Integer.parseInt(text));
                    }
                    else if (qName.equals("dir"))
                        ((SB_Parameter) var).setDirection(Integer.parseInt(text));
                    else if (qName.equals("initial"))
                        ((SB_Global) var).setInitial(((SB_Global) var).getInitial() + text);
                    else if (qName.equals("value"))
                        ((SB_Constant) var).setValue(((SB_Constant) var).getValue() + text);
                }
            } else if (qName.equals("name"))
            {
                if (_qNames.get(size - 2).equals("behavior"))
                {
                    DefaultMutableTreeNode treeNode = _catalog.findFuncNode(text);
                    _qNames.set(size - 2, treeNode);
                    if (text.equals(_main))
                    {
                        SB_Behavior behavior = (SB_Behavior) treeNode.getUserObject();
                        _catalog._main = behavior;
                        behavior.setMain(true);
                    }
                }
            } else if (qName.equals("main"))
            {
                _main = text;
            } else if (qName.equals("index"))
            {
                SB_Polymorphism poly = (SB_Polymorphism) _qNames.get(size - 3);
                poly._indices.add(text);
            } else if (qName.equals("initial"))
            {
                _initialId = Integer.parseInt(text);
            } else if (size >= 2 && _qNames.get(size - 2) instanceof SB_Element)
            {
                SB_Element element = (SB_Element) _qNames.get(size - 2);
                if (qName.equals("id"))
                {
                    int id = Integer.parseInt(text);
                    element.setId(id);
                    if (element instanceof SB_Rectangle && id == _initialId)
                        ((SB_Rectangle) element)._initial = true;
                } else if (qName.equals("expr"))
                    element._expr = text;
                else if (qName.equals("cx"))
                    _cx = Integer.parseInt(text);
                else if (qName.equals("cy"))
                {
                    _cy = Integer.parseInt(text);
                    element.setCenter(_cx, _cy);
                } else if (qName.equals("final"))
                    ((SB_Rectangle) element)._final = Integer.parseInt(text) != 0;
            } else if (size >= 2 && _qNames.get(size - 2).equals("start"))
            {
                if (qName.equals("id"))
                    _startId = Integer.parseInt(text);
                else if (qName.equals("type"))
                {
                    int startType = Integer.parseInt(text);
                    SB_Polymorphism poly = (SB_Polymorphism) _qNames.get(size - 4);
                    SB_Element startElement = poly._elements.findById(_startId, startType);
                    _qNames.set(size - 2, startElement);
                } else if (qName.equals("x"))
                    _x1 = Integer.parseInt(text);
                else if (qName.equals("y"))
                    _y1 = Integer.parseInt(text);
            } else if (size >= 2 && _qNames.get(size - 2) instanceof SB_Connector)
            {
                SB_Connector connector = (SB_Connector) _qNames.get(size - 2);
                if (qName.equals("id"))
                    connector.setId(Integer.parseInt(text));
                else if (qName.equals("priority"))
                {
                    int priority = Integer.parseInt(text);
                    connector.setPriority(priority);
                    if (priority != -1)
                    {
                        SB_Element startElement = (SB_Element) _qNames.get(size - 3);
                        if (startElement._connectors.size() < priority)
                            startElement._connectors.setSize(priority);
                        startElement._connectors.set(priority - 1, connector);
                    }
                } else if (qName.equals("interrupt"))
                    connector._interrupt = Integer.parseInt(text) != 0;
            } else if (size >= 2 && _qNames.get(size - 2).equals("end"))
            {
                if (qName.equals("id"))
                    _endId = Integer.parseInt(text);
                else if (qName.equals("type"))
                {
                    int endType = Integer.parseInt(text);
                    SB_Polymorphism poly = (SB_Polymorphism) _qNames.get(size - 6);
                    SB_Element endElement = poly._elements.findById(_endId, endType);
                    SB_Connector connector = (SB_Connector) _qNames.get(size - 3);
                    connector._endElement = endElement;
                } else if (qName.equals("x"))
                    _x2 = Integer.parseInt(text);
                else if (qName.equals("y"))
                {
                    _y2 = Integer.parseInt(text);
                    SB_Connector connector = (SB_Connector) _qNames.get(size - 3);
                    connector.p2.setLocation(_x2, _y2);
                }
            } else if (size >= 2 && _qNames.get(size - 2) instanceof SB_Binding)
            {
                SB_Binding binding = (SB_Binding) _qNames.get(size - 2);
                if (qName.equals("var"))
                    binding.setVar(text);
                else if (qName.equals("expr"))
                    binding.setExpr(text);
            } else if (qName.equals("sim"))
                _fileSIM = new File(text);
            _text = null;
        }

        public void skippedEntity(String name)
        {
        }

        public void processingInstruction(String target, String data)
        {
        }

        public void ignorableWhitespace(char[] ch, int start, int length)
        {
        }

        public void startPrefixMapping(String prefix, String uri)
        {
        }

        public void endPrefixMapping(String prefix)
        {
        }

        public void setDocumentLocator(Locator locator)
        {
        }

        // //////////////////////////////////////////////////////////////////
        // ErrorHandler events.
        // //////////////////////////////////////////////////////////////////

        public void error(SAXParseException exception)
        {
            System.err.println("SAXParseException: error");
        }

        public void fatalError(SAXParseException exception)
        {
            System.err.println("SAXParseException: fatalError");
        }

        public void warning(SAXParseException exception)
        {
            System.err.println("SAXParseException: warning");
        }
    }

    class MergeXMLHandler extends SimBionicXMLHandler
    {
        public void startDocument()
        {
            setSelectedIndex(0);

            _qNames = new Stack();
        }

        public void endDocument()
        {
            ((DefaultTreeModel) _catalog.getModel()).reload();
            ((DefaultTreeModel) _descriptors.getModel()).reload();
            _descriptors.expandAll();

            _qNames = null;
            _text = null;
            _main = null;
        }
    }

    private class SB_SettingsDialog extends JDialog
    {

        private JTextField _execTextField;

        private JTextField _argsTextField;

        private JTextField _simTextField;

        private JTextField _ipTextField;
        private JCheckBox _loopbackCheckBox;
        
        private JCheckBox _doCPPCompileCheckBox;

        private JFileChooser _fcSIM;

        public SB_SettingsDialog()
        {
            super(ComponentRegistry.getFrame(), "Project Settings", true);

            JPanel editPanel = new JPanel();
            editPanel.setLayout(new BoxLayout(editPanel, BoxLayout.Y_AXIS));
            editPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

            JLabel label = new JLabel("Simulation executable:");
            label.setEnabled(false);
            editPanel.add(label);
            editPanel.add(Box.createRigidArea(new Dimension(0, 5)));
            JPanel execPanel = new JPanel();
            execPanel.setLayout(new BoxLayout(execPanel, BoxLayout.X_AXIS));
            execPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
            _execTextField = new JTextField(10);
            _execTextField.setMaximumSize(new Dimension(1000, 20));
            _execTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
           _execTextField.setEnabled(false);
            execPanel.add(_execTextField);
            execPanel.add(Box.createRigidArea(new Dimension(7, 0)));
            JButton execButton = new JButton("...");
            execButton.setMaximumSize(new Dimension(25, 20));
            execButton.setPreferredSize(new Dimension(25, 20));
            execButton.setFocusPainted(false);
            execButton.setEnabled(false);
            execPanel.add(execButton);
            editPanel.add(execPanel);
            editPanel.add(Box.createRigidArea(new Dimension(0, 7)));

            label = new JLabel("Command line arguments:");
            label.setEnabled(false);
            editPanel.add(label);
            editPanel.add(Box.createRigidArea(new Dimension(0, 5)));
            JPanel argsPanel = new JPanel();
            argsPanel.setLayout(new BoxLayout(argsPanel, BoxLayout.X_AXIS));
            argsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
            _argsTextField = new JTextField(10);
            _argsTextField.setMaximumSize(new Dimension(1000, 20));
            _argsTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
           
            _argsTextField.setEnabled(false);
            argsPanel.add(_argsTextField);
            argsPanel.add(Box.createRigidArea(new Dimension(32, 0)));
            editPanel.add(argsPanel);
            editPanel.add(Box.createRigidArea(new Dimension(0, 7)));

            label = new JLabel("SIM file pathname:");
            
            editPanel.add(label);
            editPanel.add(Box.createRigidArea(new Dimension(0, 5)));

            JPanel simPanel = new JPanel();
            simPanel.setLayout(new BoxLayout(simPanel, BoxLayout.X_AXIS));
            simPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
            _simTextField = new JTextField(10);
            _simTextField.setMaximumSize(new Dimension(1000, 20));
            _simTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
            _simTextField.addActionListener(new ActionListener()
            {

                public void actionPerformed(ActionEvent event)
                {
                    updateSettings();
                }
            });
            
            simPanel.add(_simTextField);
            simPanel.add(Box.createRigidArea(new Dimension(7, 0)));
            JButton simButton = new JButton("...");
            simButton.setMaximumSize(new Dimension(25, 20));
            simButton.setPreferredSize(new Dimension(25, 20));
            simButton.setFocusPainted(false);

            simButton.addActionListener(new ActionListener()
            {

                public void actionPerformed(ActionEvent event)
                {
                    String text = _simTextField.getText();
                    if (text.length() > 0)
                        getFileChooserSIM().setSelectedFile(new File(text));
                    getFileChooserSIM().setDialogTitle("SIM File Pathname");
                    int returnVal = getFileChooserSIM().showDialog(ComponentRegistry.getFrame(),
                        "Set");
                    if (returnVal == JFileChooser.APPROVE_OPTION)
                    {
                        File fileSIM = getFileChooserSIM().getSelectedFile();
                        String ext = getExt(fileSIM);
                        if (ext == null)
                            fileSIM = new File(fileSIM.getPath() + ".sim");
                        _simTextField.setText(fileSIM.getAbsolutePath());
                    }
                }
            });
            
            simPanel.add(simButton);
            editPanel.add(simPanel);
            editPanel.add(Box.createRigidArea(new Dimension(0, 9)));

            label = new JLabel("Server IP address:");
            editPanel.add(label);
            editPanel.add(Box.createRigidArea(new Dimension(0, 5)));
            JPanel ipPanel = new JPanel();
            ipPanel.setLayout(new BoxLayout(ipPanel, BoxLayout.X_AXIS));
            ipPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
            _ipTextField = new JTextField(10);
            _ipTextField.setMaximumSize(new Dimension(1000, 20));
            _ipTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
            _ipTextField.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent event)
                {
                    updateSettings();
                }
            });
            ipPanel.add(_ipTextField);
            ipPanel.add(Box.createHorizontalStrut(5));
            _loopbackCheckBox = new JCheckBox("Loopback");
            _loopbackCheckBox.setFocusPainted(false);
            _loopbackCheckBox.setSelected(true);
            _loopbackCheckBox.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent event)
                {
                    if (_loopbackCheckBox.isSelected())
                    {
                        _ipTextField.setText(LOOPBACK_ADDR);
                        _ipTextField.setEnabled(false);
                    } else
                        _ipTextField.setEnabled(true);
                }
            });
            ipPanel.add(_loopbackCheckBox);
            ipPanel.add(Box.createRigidArea(new Dimension(32, 0)));
            editPanel.add(ipPanel);
            
            JPanel cppPanel = new JPanel();
            cppPanel.setLayout(new FlowLayout());
            _doCPPCompileCheckBox = new JCheckBox("Compile for C++ Engine");
            _doCPPCompileCheckBox.setFocusPainted(false);
            _doCPPCompileCheckBox.setSelected(true);
            _doCPPCompileCheckBox.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent event)
                {
                    if (_doCPPCompileCheckBox.isSelected())
                    {
                    }
                    else
                    {
                     //   _ipTextField.setEnabled(true);
                    }
                }
            });
            cppPanel.add(_doCPPCompileCheckBox);
            editPanel.add(cppPanel);
            
            editPanel.add(Box.createRigidArea(new Dimension(0, 7)));

            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
            buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
            buttonPanel.add(Box.createHorizontalGlue());
            JButton settingsOK = new JButton("OK");
            settingsOK.setFocusPainted(false);
            settingsOK.addActionListener(new ActionListener()
            {

                public void actionPerformed(ActionEvent event)
                {
                    updateSettings();
                }
            });
            buttonPanel.add(settingsOK);
            buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
            JButton settingsCancel = new JButton("Cancel");
            settingsCancel.setFocusPainted(false);
            settingsCancel.addActionListener(new ActionListener()
            {

                public void actionPerformed(ActionEvent event)
                {
                    SB_SettingsDialog.this.setVisible(false);
                }
            });
            buttonPanel.add(settingsCancel);

            getContentPane().add(editPanel, BorderLayout.CENTER);
            getContentPane().add(buttonPanel, BorderLayout.SOUTH);
            pack();

            Dimension dialogSize = getSize();
            dialogSize.width = 375;
            setSize(dialogSize);
            Rectangle frameBounds = ComponentRegistry.getFrame().getBounds();
            setLocation(frameBounds.x + (frameBounds.width - dialogSize.width) / 2, frameBounds.y
                    + (frameBounds.height - dialogSize.height) / 2);
        }

        public void initDialog()
        {
            if (_fileSIM != null)
                _simTextField.setText(_fileSIM.toString());
            else
                _simTextField.setText(null);
            _simTextField.selectAll();
            _simTextField.requestFocus();

            _ipTextField.setText(_ipAddress);
            _loopbackCheckBox.setSelected(_useLoopback);
            _ipTextField.setEnabled(!_useLoopback);
            _doCPPCompileCheckBox.setSelected(_doCPPCompile);
        }

        private void updateSettings()
        {
            String text = _simTextField.getText();
            int length = text.length();
            if (length > 0)
            {
                if (length < 4
                        || !text.trim().substring(length - 4, length).toLowerCase().equals(".sim"))
                    text += ".sim";

                if (_fileSIM == null || !_fileSIM.getAbsolutePath().equals(text))
                {
                    _fileSIM = new File(text);
                    setSBPModified(true);
                }
            }
            text = _ipTextField.getText();
            boolean useLoopback = _loopbackCheckBox.isSelected();
            if (text.equals(LOOPBACK_ADDR))
                useLoopback = true;
            if (!text.equals(_ipAddress))
            {
                _ipAddress = text;
                _useLoopback = useLoopback;
                setSBPModified(true);
            }
            
            if (_doCPPCompile != _doCPPCompileCheckBox.isSelected())
            {
            	_doCPPCompile = !_doCPPCompile;
            	setSBPModified(true);
            }
            
            setVisible(false);
        }

        JFileChooser getFileChooserSIM()
        {
            if (_fcSIM == null)
            {
                _fcSIM = new JFileChooser();
                _fcSIM.setFileFilter(new javax.swing.filechooser.FileFilter()
                {

                    public String getDescription()
                    {
                        return "SIM File (*.sim)";
                    }

                    public boolean accept(File f)
                    {
                        if (f.isDirectory())
                        {
                            return true;
                        }

                        String ext = SB_ProjectBar.this.getExt(f);
                        if (ext != null)
                        {
                            if (ext.equals("sim"))
                            {
                                return true;
                            } else
                            {
                                return false;
                            }
                        }
                        return false;
                    }
                });
                _fcSIM.setAcceptAllFileFilterUsed(false);
                _fcSIM.setCurrentDirectory(new File(System.getProperty("user.dir")));
            }
            return _fcSIM;
        }
    }

    /**
     * @return Returns the descriptors.
     */
    public SB_Descriptors getDescriptors()
    {
        return _descriptors;
    }

    /**
     * @param descriptors
     *            The descriptors to set.
     */
    public void setDescriptors(SB_Descriptors descriptors)
    {
        _descriptors = descriptors;
    }

    /**
     * Sets the main project file for saving.
     * @param baseFilename
     */
    public void setSBPFile(File baseFilename)
    {
        _fileSBP = baseFilename;
        setupFiles();
    }
    
    public void setupTypeManager(){
        _typeManager = new SB_TypeManager();
        _catalog.setTypeManager(_typeManager);
        _localsTree.setTypeManager(_typeManager);
        _descriptors.setTypeManager(_typeManager);
        SB_ParserEditorInterface.getParser().setTypeManager(_typeManager);
        _typeManager.initialize();
    }
    
    public SB_TypeManager getTypeManager(){
    	return _typeManager;
    }
    
    private void mergeFiles(File out, File[] ins)
    {
    	FileOutputStream outStream = null;
    	try{
        	outStream = new FileOutputStream(out);
    	}
    	catch (FileNotFoundException ex){
    		System.err.println("file not found: " + out.getAbsolutePath());
    		return;
    	}
    	
    	int len;
    	byte[] buf = new byte[102400];//100k buffer
    	SequenceInputStream seqStream = new SequenceInputStream(new ListOfFiles(ins));
    	
    	try{
        	while ((len = seqStream.read(buf)) >= 0)
        		outStream.write(buf, 0, len);
        	
        	outStream.flush();
        	outStream.close();
        	seqStream.close();
    	}
    	catch (IOException ex){
    		System.err.println("i/o exception");
    	}
    }
    
    class ListOfFiles implements Enumeration
    {
        private File[] listOfFiles;
        private int current = 0;

        public ListOfFiles(File[] listOfFiles)
        {
            this.listOfFiles = listOfFiles;
        }

        public boolean hasMoreElements()
        {
            if (current < listOfFiles.length)
                return true;
            else
                return false;
        }

        public Object nextElement()
        {
            FileInputStream in = null;

            if (!hasMoreElements())
                throw new NoSuchElementException("No more files.");
            else
            {
                File nextElement = listOfFiles[current];
                current++;
                try
                {
                    in = new FileInputStream(nextElement);
                }
                catch (FileNotFoundException e)
                {
                	System.err.println("ListOfFiles: Can't open " + nextElement);
                }
            }
            return in;
        }
    }

    /**
     * Notifies listeners that a behavior has been renamed.
     * @param behavior
     */
    public void behaviorRenamed(SB_Behavior behavior, String oldName)
    {
        Iterator listenerIt = _changeListeners.iterator();
        while (listenerIt.hasNext())
        {
            ((SB_ChangeListener)listenerIt.next()).behaviorRenamed(behavior,oldName);
        }
    }

    /**
     * Sets the IP address to be used for the server in debug mode.
     * @param serverAddress
     */
    public void setDebugServerIP(String serverAddress)
    {
        _ipAddress = serverAddress;
    }
    

    /**
     * Retrieves current debug server state for the given entity.
     * @param entity
     */
    void queryEntityState(SB_Entity entity) 
    {
        _querying = true;
        
        System.out.println("--> querying entity " + entity);
        
        // retrieve current stack size for the entity
        DMFieldMap fields = new DMFieldMap();
        fields.ADD_ID_FIELD("entity", entity._entityId);            
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_GET_ENTITY, fields) );
        SB_DebugMessage entityInfoMsg = waitForMessage(SB_DebugMessage.kDBG_ENG_ENTITY_INFO, -1, 10);
        int stackSize = entityInfoMsg.GetIntField("stackSize");
        long alive = entityInfoMsg.GetLongField("alive");
        
        entity._currentFrame = stackSize-1;
        entity._alive = alive;

        // retrieve information for each frame in the stack
        Vector stack = new Vector();
        for (int i=0; i<stackSize; ++i) {
            fields = new DMFieldMap();
            fields.ADD_ID_FIELD("entity", entity._entityId);
            fields.ADD_INT_FIELD("frame",i+1);
            sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_GET_FRAME, fields) );
            
            SB_DebugMessage frameInfoMsg = waitForMessage(SB_DebugMessage.kDBG_ENG_FRAME_INFO, -1, 10);
            SB_Frame newFrame = _debugger.makeFrame(
                    frameInfoMsg.GetIntField("parent"),
                    frameInfoMsg.GetStringField("behavior"),
                    frameInfoMsg.GetStringArrayField("polyIndices"),
                    frameInfoMsg.GetIntField("currentNode"),
                    frameInfoMsg.GetIntField("interrupt"));
            
            stack.add(newFrame);
        }
        entity._frames = stack;
        
        // local and global variable values for the entity are queried
        // when the entity is selected in the debugger

        _querying = false;            
    }

    /**
     * Retrieve current global variable values for the given entity.
     * Assumes that entityId is the currently-selected entity.
     * @param entityId
     */
    public void queryEntityGlobals(long entityId)
    {
        _querying = true;
        
        System.out.println("--> querying globals for entity " + entityId);

        DMFieldMap fields = new DMFieldMap();
        fields.ADD_ID_FIELD("entity", entityId);
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_GET_GLOBAL_VARS, fields) );
        SB_DebugMessage globalVarsMsg = waitForMessage(SB_DebugMessage.kDBG_ENG_GLOBAL_VARS_INFO, -1, 10);
        ArrayList globalNames = globalVarsMsg.GetStringArrayField("varNames");
        Vector globalValues = (Vector) globalVarsMsg.GetField("varValues");

        _querying = false;

        _debugger.updateGlobalVariables(globalNames,globalValues);
    }
        
    /**
     * Retrieve current global variable values for the given entity and stack frame.
     * Assumes that entityId is the currently-selected entity.
     * @param entityId
     * @param frame
     */
    public void queryEntityLocals(long entityId,int frame)
    {
        _querying = true;

        System.out.println("--> querying locals for entity " + entityId + ", frame " + frame);

        DMFieldMap fields = new DMFieldMap();
        fields.ADD_ID_FIELD("entity", entityId);
        fields.ADD_INT_FIELD("frame", frame+1);
        sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_GET_LOCAL_VARS, fields) );
        SB_DebugMessage localVarsMsg = waitForMessage(SB_DebugMessage.kDBG_ENG_LOCAL_VARS_INFO, -1, 10);
        ArrayList localNames = localVarsMsg.GetStringArrayField("varNames");
        Vector localValues = (Vector) localVarsMsg.GetField("varValues");
        
        _querying = false;
        
        _debugger.updateLocalVariables(localNames, localValues);
    }

    /**
     * Periodically checks for and processes unsolicited messages from the 
     * debug server. Solicited messages -- those in response to a specific
     * query from the client -- are handled in 
     * 
     */
    private final class DebugServerListener implements ActionListener
    {
        public void actionPerformed(ActionEvent evt) {
            if (_querying) return;
            
            SB_DebugMessage msg;
            try {
                msg = receiveMsg();
            } catch (IOException e) {
                _logger.log(e.toString());
                SB_OutputBar._debug.addLine(new SB_Line("Connection to server failed!",Color.red));
                closeDebug();
                return;
            }
            
            if (msg == null)
                return;
            
            switch (msg.GetMsgType())
            {
            case SB_DebugMessage.kDBG_ENG_BEHAVIOR_CHANGED:
            {
                long entityId = msg.GetIdField("entity");
                String behav_name = msg.GetStringField("behavior");
                ArrayList polyIndices = msg.GetStringArrayField("polyIndices");
                // node will be set at the end of the step when the stack is refreshed
                
                _debugger.changeBehavior(entityId,behav_name,polyIndices);
                
                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " reset behavior stack to '" + behav_name + "'",_transcriptColor));
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_BREAKPOINT_HIT:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                int breakpointId = msg.GetIntField("breakpointId");
                
                stepFinished(entityId,frame);
                
                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " hit breakpoint " + breakpointId,_transcriptColor));
            }
            break;

            case SB_DebugMessage.kDBG_ENG_CONDITION_CHECKED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                int conditionId = msg.GetIntField("conditionId");
                SB_Param conditionValue = msg.GetParamField("conditionValue");
                
                _debugger.checkCondition(entityId, frame, conditionId, conditionValue);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " checked condition " + conditionId + " on frame " + frame + ", value = '" + conditionValue + "'",_transcriptColor));
            }
            break;

            case SB_DebugMessage.kDBG_ENG_CONDITION_FOLLOWED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                int conditionId = msg.GetIntField("conditionId");
                
                _debugger.followCondition(entityId, frame, conditionId);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " followed condition " + conditionId + " on frame " + frame,_transcriptColor));
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_ENTITY_CREATED:
            {
                long entityId = msg.GetIdField("entity");
                String name = msg.GetStringField("name");
                _debugger.createEntity(entityId, name);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " (" + name + ") created",_transcriptColor));
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_ENTITY_DESTROYED:
            {
                long entityId = msg.GetIdField("entity");                
                _debugger.destroyEntity(entityId);              

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " destroyed",_transcriptColor));
            }
            break;            

            case SB_DebugMessage.kDBG_ENG_ENTITY_ENDING:
            {
                long entityId = msg.GetIdField("entity");                
                _debugger.endEntity(entityId);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " completed tick",_transcriptColor));
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_ENTITY_STARTING:
            {
                long entityId = msg.GetIdField("entity");
                _debugger.startEntity(entityId);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " started tick",_transcriptColor));
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_FRAME_COMPLETED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                
                _debugger.completeFrame(entityId, frame);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " completed frame " + frame,_transcriptColor));
            }
            break;

            case SB_DebugMessage.kDBG_ENG_FRAME_CREATED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                int parent = msg.GetIntField("parent");
                String behav_name = msg.GetStringField("behavior");
                ArrayList polyIndices = msg.GetStringArrayField("polyIndices");
                int currentNode = msg.GetIntField("currentNode");
                int interrupt = msg.GetIntField("interrupt");
                
                _debugger.createFrame(entityId, frame, parent, behav_name, polyIndices, currentNode, interrupt);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " created frame " + frame + " : " + behav_name,_transcriptColor));
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_FRAME_CURRENT:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
            
                _debugger.setCurrentFrame(entityId,frame);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " has current frame " + frame,_transcriptColor));
            }
            break;
                
            case SB_DebugMessage.kDBG_ENG_FRAME_DISCARDED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                
                _debugger.discardFrame(entityId, frame);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " discarded frame " + frame,_transcriptColor));
            }
            break;
                
            case SB_DebugMessage.kDBG_ENG_GLOBAL_CHANGED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                String varName = msg.GetStringField("varName");
                SB_Param value = msg.GetParamField("value");

                _debugger.changeGlobalVariable(entityId,frame,varName,value);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " global variable '" + varName + "' has new value '" + value + "'",_transcriptColor));
            }
            break;
                
            case SB_DebugMessage.kDBG_ENG_NODE_CHANGED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                int nodeId = msg.GetIntField("nodeId");
                
                _debugger.changeNode(entityId, frame, nodeId);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " changed current node in frame " + frame + " to " + nodeId,_transcriptColor));
            }
            break;
                            
            case SB_DebugMessage.kDBG_ENG_SHUTDOWN:
            {
                // acknowledge shutdown from engine
                DMFieldMap fields = new DMFieldMap();
    
                sendMsg( new SB_DebugMessage(SB_DebugMessage.kDBG_GUI_SHUTDOWN_OK, fields) );

                SB_OutputBar._debug.addLine(new SB_Line("Received shutdown request from engine"));
            
                closeDebug();
            }
            break;
            
            case SB_DebugMessage.kDBG_ENG_STEP_FINISHED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                long alive = msg.GetLongField("alive");
                int query = msg.GetIntField("query");

                stepFinished(entityId,frame);

                SB_OutputBar._debug.addLine(new SB_Line("--- STEP COMPLETE ---",_transcriptColor));
                _transcriptColor = (_transcriptColor == Color.BLACK) ? Color.DARK_GRAY : Color.BLACK;
            }
            break;

            case SB_DebugMessage.kDBG_ENG_VAR_CHANGED:
            {
                long entityId = msg.GetIdField("entity");
                int frame = msg.GetIntField("frame")-1;
                String varName = msg.GetStringField("varName");
                SB_Param value = msg.GetParamField("value");

                _debugger.changeVariable(entityId,frame,varName,value);

                SB_OutputBar._debug.addLine(new SB_Line("Entity " + entityId + " local variable '" + varName + "' has new value '" + value + "'",_transcriptColor));
            }
            break;
            
            default:
            {
                SB_OutputBar._debug.addLine(new SB_Line("Unhandled engine message: " + msg.GetMsgType(),Color.RED));
            }
            break;
            }
            
            
            System.out.println("<-- " + msg.GetTypeName());
            System.out.println(_debugger.toString());
            if (_debugMode == DebugMode.PAUSE) 
                System.out.println("*************************************");
        }
        
        /**
         * Sets the specified entity to be current and updates
         * its state from the server.  Also 
         * @param entityId
         * @param frame
         */
        private void stepFinished(long entityId,int frame)
        {
            SB_Entity entity = _debugger.findEntity(entityId);
            _debugger.setCurrentEntity(entity);
            
            queryEntityState(entity);
            
            _debugger.setSelectedEntity(entity,true);            
            
            _debugMode = DebugMode.PAUSE;            
            updateDebugToolbar();
        }
    }
}

